W7. Encapsulation, Constructors, Setters & Getters, Class Attributes & Methods, Parameter Passing
1. Summary
1.1 The Core Principles of Object-Oriented Programming (OOP)
Object-Oriented Programming is a paradigm based on the concept of “objects,” which can contain data in the form of fields (often known as attributes or properties) and code in the form of procedures (often known as methods). The three main principles of OOP are Encapsulation, Inheritance, and Polymorphism. This document focuses primarily on encapsulation and the foundational concepts of creating and managing objects in Java.
1.2 Encapsulation and Information Hiding
1.2.1 Encapsulation
Encapsulation is the practice of bundling an object’s data (attributes) and the methods that operate on that data into a single unit, known as a class. The primary goal is to protect the object’s internal state from outside interference and misuse. This is achieved by hiding the internal data and exposing only the necessary functionality through a public interface. Think of a car: you don’t need to know how the engine works internally to drive it. You use the public interface—steering wheel, pedals, and gear shift—to interact with it. Encapsulation ensures the engine’s complex internal state is protected.
1.2.2 The Information Hiding Principle
This is the core concept that makes encapsulation possible. The designer of a class must explicitly specify which properties (attributes and methods) are public (accessible to other parts of the program, or “clients”) and which are private (internal and hidden from clients). The programming language then enforces these rules, ensuring that clients can only interact with the public properties.
1.2.3 Access Modifiers in Java
Java uses access modifiers to implement encapsulation and information hiding. They determine the visibility of classes, fields, and methods.
public: Accessible from any other class.private: Accessible only within the same class. This is the strictest level of access and is key to strong encapsulation.protected: Accessible within the same package and by subclasses in other packages.package-private(default): If no modifier is specified, the member is accessible only to classes within the same package.
1.3 Controlled Access: Setters and Getters
To provide controlled access to private fields, we use special public methods known as getters and setters.
- A Getter (or accessor) is a method that retrieves the value of a private field. By convention, its name starts with
getfollowed by the field name (e.g.,getAge()). It simply returns the value. - A Setter (or mutator) is a method that modifies the value of a private field. By convention, its name starts with
setfollowed by the field name (e.g.,setAge(int newAge)). Setters are crucial because they can contain logic to validate the new value before assigning it, ensuring the object remains in a valid state. For example, asetAgemethod could reject negative values.
1.4 Object Creation: Constructors
A constructor is a special method used to initialize a new object when it is created. It’s called automatically when you use the new keyword.
1.4.1 Key Properties of Constructors
- A constructor’s name must be exactly the same as the class name.
- Constructors do not have a return type, not even
void. - Their primary job is to set the initial state of the object’s fields.
- If you do not define any constructor in a class, Java provides a default, no-argument constructor that initializes fields to their default values (e.g.,
0for numbers,falsefor booleans,nullfor objects).
1.4.2 Multiple Constructors (Overloading)
A class can have multiple constructors, as long as they have different parameter lists (different number or types of parameters). This is called constructor overloading. It provides flexible ways to create an object. The constructor with no parameters is called the default constructor.
1.4.3 Refactoring with this()
To avoid duplicating code between constructors, one constructor can call another in the same class using the this() keyword. The this() call must be the very first statement in the constructor.
1.4.4 Inline Initialization
Java allows you to initialize an object’s fields directly where they are declared. When an object is created, this inline initialization is performed first, in the order the fields appear in the code. After that, the constructor is executed.
1.5 Passing Data to Methods
1.5.1 Formal vs. Actual Parameters
- Formal Parameters: The variables listed in a method’s definition. They act as placeholders.
- Actual Parameters (Arguments): The actual values or variables passed to the method when it is called.
1.5.2 Parameter Passing Mechanisms
There are two primary ways to pass parameters:
- Pass-by-Value: A copy of the argument’s value is passed to the method. Any changes made to the formal parameter inside the method do not affect the original argument.
- Pass-by-Reference: A reference (or memory address) to the argument is passed. Any changes made to the formal parameter inside the method do affect the original argument because they both point to the same data.
1.5.3 Parameter Passing in Java
Java is strictly pass-by-value. However, the behavior can be confusing.
- For primitive types (
int,char,double, etc.): The method receives a copy of the value. The original variable is safe from modification. - For object types (or reference types): The method receives a copy of the reference that points to the object. Both the original reference and the method’s copy point to the exact same object on the heap. Therefore, while the method cannot change where the original reference variable points, it can change the internal state of the object that the reference points to.
1.6 Class Members vs. Instance Members
1.6.1 Instance Members
By default, fields and methods are instance members. This means that every object (instance) created from a class gets its own separate copy of these members. For example, if you have two Person objects, each has its own name and age.
1.6.2 Class Members (Static)
The static keyword creates a class member.
- Static Fields: A static field belongs to the class itself, not to any individual instance. There is only one copy of a static field, shared among all objects of that class. For example, a
counterto track how manyPersonobjects have been created would be static. - Static Methods: A static method also belongs to the class and can be called directly on the class name (e.g.,
Math.abs()) without creating an object. Because static methods are not tied to a specific instance, they cannot access instance fields or methods directly and do not have athisreference.
1.7 Special Keywords and The main Method
1.7.1 The this Reference
The this keyword is a reference to the current object instance. It can be used inside an instance method or constructor to:
- Refer to instance fields when they are shadowed by local variables or parameters.
- Call another constructor from within a constructor using
this(...).
1.7.2 The null Reference
null is a special literal representing an empty reference. It means the reference variable does not point to any object in memory. Attempting to call a method or access a field on a null reference will result in a NullPointerException.
1.7.3 The main Method
The public static void main(String[] args) method is the entry point for any Java application. The Java Virtual Machine (JVM) starts execution here.
public: It must be accessible to the JVM.static: The JVM must be able to call it without creating an object of the class.void: It does not return any value.String[] args: It accepts an array of strings, which are command-line arguments passed to the program.
2. Definitions
- Encapsulation: The bundling of data (attributes) and methods that operate on the data into a single unit (a class), and restricting access to an object’s internal components.
- Access Modifier: Keywords in Java (
public,private,protected, default) that set the access level for classes, attributes, and methods. - Getter: A public method used to retrieve the value of a private field.
- Setter: A public method used to modify the value of a private field, often including validation logic.
- Constructor: A special method for creating and initializing an object instance of a class.
- Constructor Overloading: Defining multiple constructors in the same class with different parameter lists.
- Pass-by-Value: A parameter passing mechanism where a copy of the actual parameter’s value is sent to the method. The original data is not affected.
- Pass-by-Reference: A parameter passing mechanism where a reference to the actual parameter is sent to the method. The original data can be modified. (Note: Java uses pass-by-value exclusively, but the value for objects is a reference.)
- Static Field: A variable that belongs to the class as a whole, not to individual instances. All instances share this one variable.
- Static Method: A method that belongs to the class as a whole and can be called without creating an instance of the class. It cannot access instance-specific members.
this: A keyword that acts as a reference to the current object instance.null: A special value indicating that a reference variable does not point to any object.mainmethod: The special static method that serves as the entry point for a Java application.
3. Examples
3.1. Calculator Class (Lab 7, Task 1)
Create a class which implements a Calculator. The main method should receive three parameters from the command line: a number, an operation symbol (+, -, *, /), and a second number. Handle division by zero.
Click to see the solution
public class Calculator {
// Method for addition.
public double add(double a, double b) {
return a + b;
}
// Method for subtraction.
public double subtract(double a, double b) {
return a - b;
}
// Method for multiplication.
public double multiply(double a, double b) {
return a * b;
}
// Method for division with error handling.
public double divide(double a, double b) {
if (b == 0) {
System.out.println("Error: Division by zero.");
return -1.0; // Return -1 as specified on error.
}
return a / b;
}
public static void main(String[] args) {
// Check if exactly three command-line arguments are provided.
if (args.length != 3) {
System.out.println("Usage: java Calculator <number1> <operator> <number2>");
return;
}
// Create an instance of the Calculator class.
Calculator calc = new Calculator();
// Parse the string arguments into numbers.
double num1 = Double.parseDouble(args[0]);
String operator = args[1];
double num2 = Double.parseDouble(args[2]);
double result = 0;
// Use a switch statement to perform the correct operation.
switch (operator) {
case "+":
result = calc.add(num1, num2);
break;
case "-":
result = calc.subtract(num1, num2);
break;
case "*":
result = calc.multiply(num1, num2);
break;
case "/":
result = calc.divide(num1, num2);
break;
default:
System.out.println("Error: Invalid operator. Use +, -, *, or /.");
return;
}
// Print the result.
System.out.println("Result: " + result);
}
}3.3. Time Class with Chaining (Lab 7, Task 3)
Create a class Time which receives the time. Create 2 instances of Time and calculate the difference between these times. The Time class should have a method to advance the time by one second and return this instance to support chaining.
Click to see the solution
// Time.java
class Time {
private int hours;
private int minutes;
private int seconds;
public Time(int hours, int minutes, int seconds) {
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
}
// Method to advance time by one second.
public Time inc() {
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
if (hours >= 24) {
hours = 0;
}
}
}
// Return 'this' to allow for method chaining.
return this;
}
// Convert to total seconds for easy difference calculation.
private int toTotalSeconds() {
return hours * 3600 + minutes * 60 + seconds;
}
// Static method to find the difference.
public static Time difference(Time t1, Time t2) {
int diffInSeconds = Math.abs(t1.toTotalSeconds() - t2.toTotalSeconds());
int h = diffInSeconds / 3600;
int m = (diffInSeconds % 3600) / 60;
int s = diffInSeconds % 60;
return new Time(h, m, s);
}
@Override
public String toString() {
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
}
// TimeCreator.java
public class TimeCreator {
public static void main(String[] args) {
// Create 2 instances of Time.
Time t1 = new Time(14, 20, 58);
Time t2 = new Time(16, 10, 0);
System.out.println("Time 1: " + t1);
System.out.println("Time 2: " + t2);
// Calculate the difference.
Time diff = Time.difference(t1, t2);
System.out.println("Difference: " + diff);
// Demonstrate chaining.
System.out.println("\nAdvancing Time 1 by 3 seconds:");
System.out.println("Original: " + t1);
t1.inc().inc().inc(); // Chaining the inc() method.
System.out.println("New Time: " + t1);
}
}